package club.janna.jrpc.util;

import club.janna.jrpc.annotation.SPIService;
import club.janna.jrpc.cluster.loadbalance.LoadBalancer;
import club.janna.jrpc.register.Register;
import club.janna.jrpc.remoting.Transporter;
import club.janna.jrpc.rpc.protocol.Protocol;
import club.janna.jrpc.serialize.Serialization;
import org.springframework.util.StringUtils;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

/**
 * SPI服务提供者
 * 该类为线程不安全，多线程情况下使用需要额外的同步操作
 * @author Panbo.Guo
 */
public class SPIServiceProvider {

    /**
     * 单例池
     */
    private static final Map<Class<?>, Map<String, Object>> singletonMap = new HashMap<>();

    /**
     * 获取传输层
     * @param serviceName
     * @return
     */
    public static Transporter getTransporter(String serviceName) {
        return getService(serviceName, Transporter.class);
    }

    /**
     * 获取协议
     * @param serviceName
     * @return
     */
    public static Protocol getProtocol(String serviceName) {
        return getService(serviceName, Protocol.class);
    }

    /**
     * 获取序列化器
     * @param serviceName
     * @return
     */
    public static Serialization getSerialization(String serviceName) {
        return getService(serviceName, Serialization.class);
    }

    /**
     * 获取注册服务
     * @param uri
     * @return
     */
    public static Register getRegister(URI uri) {
        Register register = getService(uri.getScheme(), Register.class);
        register.init(uri);
        return register;
    }

    /**
     * 获取负载均衡器
     * @param serviceName
     * @return
     */
    public static LoadBalancer getLoadBalancer(String serviceName) {
        return getService(serviceName, LoadBalancer.class, false);
    }

    /**
     * 获取服务
     * 默认使用缓存
     * @param serviceName
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> T getService(String serviceName, Class<T> tClass) {
        return getService(serviceName, tClass, true);
    }

    /**
     * 获取服务
     * @param serviceName
     * @param tClass
     * @param <T>
     * @param cached 是否需要缓存
     * @return
     */
    public static <T> T getService(String serviceName, Class<T> tClass, boolean cached) {
        if(!StringUtils.hasLength(serviceName))
            throw new RuntimeException("service name is null!");

        // 如果单例池中存在，直接返回
        if(cached) {
            if (singletonMap.containsKey(tClass)) {
                Map<String, Object> stringObjectMap = singletonMap.get(tClass);
                if (stringObjectMap.containsKey(serviceName))
                    return (T) stringObjectMap.get(serviceName);
            }
        }

        ServiceLoader<T> loader = ServiceLoader.load(tClass);
        for (T t : loader) {
            if(match(t, serviceName)) {
                // 放入单例池中
                if(cached) {
                    Map<String, Object> stringObjectMap = singletonMap.computeIfAbsent(tClass, k -> new HashMap<>());
                    stringObjectMap.put(serviceName, t);
                }
                return t;
            }
        }
        throw new RuntimeException("service [" + serviceName + "] not found!");
    }

    /**
     * 匹配SPI服务
     * @param t
     * @param serviceName
     * @return
     */
    private static boolean match(Object t, String serviceName) {
        if(t == null)
            return false;
        Class<?> c = t.getClass();
        SPIService spiService = c.getAnnotation(SPIService.class);
        if(spiService == null)
            return c.getSimpleName().equals(serviceName);
        else
            return spiService.value().equals(serviceName) || c.getSimpleName().equals(serviceName);
    }
}
